/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

import 'api/device_orientation_api.dart';
import 'breakpoint/breakpoint.dart';
import 'breakpoint/breakpoint_manager.dart';
import 'rotation_rules.dart';

typedef ScreenRotationCallback = void Function(Orientation orientation);

class AdaptiveRotation {
  AdaptiveRotation._internal();

  factory AdaptiveRotation() => _instance;
  static final AdaptiveRotation _instance = AdaptiveRotation._internal();

  final _breakPointManager = BreakpointManager();
  final Map<WidthBreakpoint, Map<HeightBreakpoint, BreakPointRange>>
      _breakPointMap = {
    WidthBreakpoint.xs: {
      HeightBreakpoint.sm: BreakPointRange.xSmallLandscape,
      HeightBreakpoint.md: BreakPointRange.xSmallSquare,
      HeightBreakpoint.lg: BreakPointRange.xSmallPortrait,
    },
    WidthBreakpoint.sm: {
      HeightBreakpoint.sm: BreakPointRange.smallLandscape,
      HeightBreakpoint.md: BreakPointRange.smallSquare,
      HeightBreakpoint.lg: BreakPointRange.smallPortrait,
    },
    WidthBreakpoint.md: {
      HeightBreakpoint.sm: BreakPointRange.mediumLandscape,
      HeightBreakpoint.md: BreakPointRange.mediumSquare,
      HeightBreakpoint.lg: BreakPointRange.mediumPortrait,
    },
    WidthBreakpoint.lg: {
      HeightBreakpoint.sm: BreakPointRange.largeLandscape,
      HeightBreakpoint.md: BreakPointRange.largeSquare,
      HeightBreakpoint.lg: BreakPointRange.largePortrait,
    },
    WidthBreakpoint.xl: {
      HeightBreakpoint.sm: BreakPointRange.xLargeLandscape,
      HeightBreakpoint.md: BreakPointRange.xLargeSquare,
      HeightBreakpoint.lg: BreakPointRange.xLargePortrait,
    },
  };
  final Set<ScreenRotationCallback> _screenRotationCallbacks = {};
  StreamSubscription<int>? _gravityOrientation;
  StreamSubscription<int>? _screenOrientation;
  DeviceOrientation _lastGravityDirection = DeviceOrientation.portraitUp;
  OrientationType _lastOrientationType = OrientationType.unKnown;
  Orientation _lastScreenOrientation = Orientation.portrait;

  Future<void> registerEvent() async {
    if (await DeviceOrientationApi.isSensorAvailable()) {
      _gravityOrientation = DeviceOrientationApi.gravityOrientation
          .listen(_handleGravityOrientationEvent);
    }
    _screenOrientation = DeviceOrientationApi.screenOrientation
        .listen(_handleScreenOrientationEvent);
  }

  /// 注册屏幕方向变化时间
  void addRotationListener(ScreenRotationCallback listener) {
    _screenRotationCallbacks.add(listener);
  }

  void removeRotationListener(ScreenRotationCallback? listener) {
    if (listener == null) {
      _screenRotationCallbacks.clear();
    } else {
      _screenRotationCallbacks.remove(listener);
    }
  }

  /// 注销监听，初始化缓存参数
  void unregisterEvent() {
    _lastGravityDirection = DeviceOrientation.portraitUp;
    _gravityOrientation?.cancel();
    _gravityOrientation = null;
    _screenOrientation?.cancel();
    _screenOrientation = null;
    removeRotationListener(null);
  }

  /// 设置重力传感器刷新频率
  void setGravityUpdateInterval(int interval) {
    DeviceOrientationApi.setGravityUpdateInterval(interval);
  }

  /// 进入后台暂停监听
  void pauseEvent() {
    _gravityOrientation?.pause();
    _screenOrientation?.pause();
  }

  /// 进入前台恢复监听
  void resumeEvent() {
    _gravityOrientation?.resume();
    _screenOrientation?.resume();
  }

  /// 设置正常浏览视频场景的屏幕旋转属性，窗口尺寸变化都要重新设置
  void setNotFullScreen({required Size videoSize, required Size screenSize}) {
    assert(videoSize.width >= 0 && videoSize.height >= 0,
        'videoSize must be non-negative');
    assert(screenSize.width >= 0 && screenSize.height >= 0,
        'screenSize must be non-negative');
    BreakPointRange range = _getBreakPointRange(screenSize);
    _setScreenRotationProperties(ScreenStatus.normalScreen, videoSize, range);
  }

  /// 设置全屏播放视频场景的屏幕旋转属性，窗口尺寸变化都要重新设置
  void setFullScreen({required Size videoSize, required Size screenSize}) {
    assert(videoSize.width >= 0 && videoSize.height >= 0,
        'videoSize must be non-negative');
    assert(screenSize.width >= 0 && screenSize.height >= 0,
        'screenSize must be non-negative');
    BreakPointRange breakPointRange = _getBreakPointRange(screenSize);
    ScreenStatus status = videoSize.width <= videoSize.height
        ? ScreenStatus.portraitFullScreen
        : ScreenStatus.landscapeFullScreen;
    _setScreenRotationProperties(status, videoSize, breakPointRange);
  }

  void _changeRotation(Map<int, OrientationType> orientationMap) {
    OrientationType? orientation = orientationMap[_lastGravityDirection.index];
    if (_lastOrientationType == orientation) {
      return;
    }
    _lastOrientationType = orientation ?? OrientationType.unKnown;
    switch (orientation) {
      case OrientationType.portrait:
        DeviceOrientationApi.setPortraitUpMode();
        break;
      case OrientationType.userRotationPortrait:
        DeviceOrientationApi.setPortraitUser();
        break;
      case OrientationType.userRotationLandscape:
        DeviceOrientationApi.setLandscapeRightUser();
        break;
      case OrientationType.userRotationLandscapeInverted:
        DeviceOrientationApi.setLandscapeLeftUser();
        break;
      case OrientationType.autoRotationUnspecified:
        DeviceOrientationApi.setAutoOrientationUser();
        break;
      default:
    }
  }

  BreakPointRange _getBreakPointRange(Size screenSize) {
    _breakPointManager.updateBreakpoint(
        width: screenSize.width, height: screenSize.height);
    WidthBreakpoint widthBreakpoint =
        _breakPointManager.getCurrentWidthBreakpoint();
    HeightBreakpoint heightBreakpoint =
        _breakPointManager.getCurrentHeightBreakpoint();
    BreakPointRange range = BreakPointRange.smallPortrait;
    if (_breakPointMap.containsKey(widthBreakpoint) &&
        _breakPointMap[widthBreakpoint]!.containsKey(heightBreakpoint)) {
      range = _breakPointMap[widthBreakpoint]![heightBreakpoint]!;
    }
    return range;
  }

  void _setScreenRotationProperties(ScreenStatus screenStatus, Size videoSize,
      BreakPointRange breakPointRange) {
    VideoDirection videoDirection = videoSize.width <= videoSize.height
        ? VideoDirection.portrait
        : VideoDirection.landscape;
    List<RotationRule>? rotationRules = rotationRulesMap[screenStatus];
    for (var rule in rotationRules!) {
      // 获取对应规则的临时旋转方向和允许旋转的所有方向
      if (rule.breakPointRanges.contains(breakPointRange) &&
          rule.videoDirection == videoDirection) {
        _changeRotation(rule.orientations);
        break;
      }
    }
  }

  /// 处理设备手持方向
  void _handleGravityOrientationEvent(int event) {
    DeviceOrientation gravityDirection;
    switch (event) {
      case 1:
        gravityDirection = DeviceOrientation.landscapeRight;
        break;
      case 2:
        gravityDirection = DeviceOrientation.portraitDown;
        break;
      case 3:
        gravityDirection = DeviceOrientation.landscapeLeft;
        break;
      default:
        gravityDirection = DeviceOrientation.portraitUp;
    }
    if (gravityDirection != _lastGravityDirection) {
      _lastGravityDirection = gravityDirection;
    }
  }

  /// 处理屏幕方向
  void _handleScreenOrientationEvent(int orientation) {
    Orientation currentOrientation =
        orientation == 0 ? Orientation.portrait : Orientation.landscape;
    if (currentOrientation != _lastScreenOrientation &&
        _screenRotationCallbacks.isNotEmpty) {
      _lastScreenOrientation = currentOrientation;
      // 拷贝一份进行安全遍历
      for (ScreenRotationCallback callback
          in _screenRotationCallbacks.toList()) {
        callback.call(_lastScreenOrientation);
      }
    }
  }
}
